package com.tsd.system.service.impl;

import com.tsd.core.constants.Constants;
import com.tsd.core.utils.HlpUtils;
import com.tsd.core.utils.ListUtil;
import com.tsd.core.vo.HlpException;
import com.tsd.core.vo.PageBean;
import com.tsd.service.BaseServiceImpl;
import com.tsd.system.SysConstants;
import com.tsd.system.dao.*;
import com.tsd.system.entity.*;
import com.tsd.system.service.SysResourceService;
import com.tsd.system.utils.SysTreeUtil;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;

/**
 * 描述：SysResource 服务实现层
 *
 * @author Hillpool
 * @date 2019/03/12 12:20:51
 */
@Service
public class SysResourceServiceImpl extends BaseServiceImpl implements SysResourceService {

    @Resource
    private SysResourceMapper sysResourceMapper;
    @Resource
    private SysRoleResourceMapper sysRoleResourceMapper;
    @Resource
    private SysUserResourceMapper sysUserResourceMapper;

    @Override
    public SysResource getSysResourceById(Long id) throws Exception {
        return sysResourceMapper.selectByPrimaryKey(id);
    }

    @Override
    public SysResourceExt queryBySid(String sid) throws Exception {
        return sysResourceMapper.selectBySid(sid);
    }

    @Override
    public void saveSysResource(SysResource record, SysUser sysUser) throws Exception {
        SysResource old = sysResourceMapper.selectOtherByCodeInSameParent(record);
        super.checkNotEmpty(old, "同一节点上已有该编号的资源");
        if (HlpUtils.isEmpty(record.getTarget_display_mode())) {
            record.setTarget_display_mode("div");
        }
        setFullCode(record);
        if (HlpUtils.isEmpty(record.getId())) {
            super.checkEmpty(record.getRes_type(), "资源类型不能为空");
            super.checkEmpty(record.getCode(), "资源编码不能为空");
            super.checkEmpty(record.getName(), "资源名称不能为空");
            record.setPre_set(0);
            super.autoInjectBaseData(record, sysUser, TYPE_CREATE);
            sysResourceMapper.insertSelective(record);
            updateRoleResourceCheckType(record.getParent_sid());
        } else {
            old = getSysResourceById(record.getId());
            super.checkEmpty(old, "记录不存在");
            super.autoInjectBaseData(record, sysUser, TYPE_UPDATE);
            //是否变更Code
            boolean hasChangedCode = !HlpUtils.isEqual(record.getCode(), old.getCode());
            //是否移动
            boolean hasMoved = !HlpUtils.isEqual(record.getParent_sid(), old.getParent_sid());
            sysResourceMapper.updateByPrimaryKeySelective(record);
            if (hasMoved) {
                updateSysResouceParentId(record.getSid(), record.getParent_sid(), sysUser);
            }
            if (hasChangedCode) {
                updateChildFullCode(record.getSid());
            }
        }
        updateChildCount(record.getParent_sid());
    }

    /**
     * 更新角色资源的全选和半选
     * 当新增资源后，需要将该资源的上级节点在角色资源中的全选改为半选，否则在菜单显示上会显示未经授权的新资源
     * 移动资源时也存在一样的问题，所以新增和移动资源时需要调用本方法
     *
     * @param parent_sid
     */
    private void updateRoleResourceCheckType(String parent_sid) {
        while (!HlpUtils.isEmpty(parent_sid) && !Constants.ROOT_PARENT_SID.equals(parent_sid)) {
            sysRoleResourceMapper.updateCheckType2Half(parent_sid);
            SysResourceExt parent = sysResourceMapper.selectBySid(parent_sid);
            if (parent != null) {
                parent_sid = parent.getParent_sid();
            }
        }
    }

    /**
     * 设置资源完整编码
     *
     * @param record
     * @throws HlpException
     */
    private void setFullCode(SysResource record) throws HlpException {
        String parentId = record.getParent_sid();
        if (HlpUtils.isEmpty(parentId) || Constants.ROOT_PARENT_SID.equals(parentId)) {
            record.setFull_code(record.getCode() + ".");
        } else {
            SysResource parent = sysResourceMapper.selectBySid(parentId);
            if (parent == null) {
                throw new HlpException("上级资源不存在，上级资源的sid：" + parentId);
            } else {
                record.setFull_code(parent.getFull_code() + record.getCode() + ".");
            }
        }
    }

    @Override
    public void deleteSysResource(Long id, SysUser sysUser) throws Exception {
        SysResource resource = getSysResourceById(id);
        deleteRelation(resource, sysUser);
    }

    private void deleteRelation(SysResource resource, SysUser sysUser) {
        sysResourceMapper.deleteByPrimaryKey(resource.getId());
        sysRoleResourceMapper.deleteByResourceSid(resource.getSid());
        sysUserResourceMapper.deleteByResSid(resource.getSid());
        List<SysResourceExt> childrens = sysResourceMapper.selectByParentSid(resource.getSid());
        if (!childrens.isEmpty()) {
            for (SysResource sysResource : childrens) {
                deleteRelation(sysResource, sysUser);
            }
        }
    }

    @Override
    public List<SysResourceExt> findSysResources(Map<String, Object> params, PageBean page) throws Exception {
        return sysResourceMapper.selectByPage(params, page);
    }

    @Override
    public List<SysResourceExt> findSysResourceTree(String parentSid) throws Exception {
        SysResourceExt parent = sysResourceMapper.selectBySid(parentSid);
        Map<String, Object> params = new HashMap<>();
        params.put("dr", 0);
        if (parent != null) {
            params.put("full_code", parent.getFull_code() + "%");
        }
        List<SysResourceExt> list = sysResourceMapper.selectByParams(params);
        return SysTreeUtil.expandResourceTree(list);
    }

    @Override
    public List<SysResourceExt> findSysResourceTree4User(String parentSid, SysUser opUser) throws Exception {
        List<SysResourceExt> resources = sysResourceMapper.select4UserId(opUser.getSid());
        List<String> validList = new ArrayList<String>();
        for (SysResource resource : resources) {
            validList.add(resource.getSid());
        }
        List<SysResourceExt> list = sysResourceMapper.selectByParentSid(parentSid);
        for (SysResourceExt resource : list) {
            expandResource(resource, validList);
        }
        return resources;
    }

    public List<SysResourceExt> list2Tree(List<SysResourceExt> arr) {
        if (arr == null || arr.size() == 0) {
            return arr;
        }
        //将数组转为Object的形式，key为数组中的id
        List<SysResourceExt> r = new ArrayList<>();
        Map<String, Object> hash = new HashMap<>();
        for (SysResource x : arr) {
            hash.put(x.getSid(), x);
        }
        // 遍历结果集
        for (SysResourceExt x : arr) {
//            hash.put(x.getSid(), x);
            // 单条记录
            SysResourceExt aVal = x;
            //在hash中取出key为单条记录中pid的值
            SysResourceExt hashVP = (SysResourceExt) hash.get(aVal.getParent_sid());
            //如果记录的pid存在，则说明它有父节点，将她添加到孩子节点的集合中
            if (hashVP != null) {
                // 检查是否有child属性
                if (hashVP.getChildrens() != null && hashVP.getChildrens().size() > 0) {
                    List<SysResourceExt> ch = hashVP.getChildrens();
                    ch.add(aVal);
                    hashVP.setChildrens(ch);
                } else {
                    List<SysResourceExt> ch = new LinkedList<>();
                    ch.add(aVal);
                    hashVP.setChildrens(ch);
                }
            } else {
                r.add(aVal);
            }
        }
        return r;
    }

    @Override
    public List<SysResourceExt> findSysResourceTreeByRole(String roleSid, String parentId) throws Exception {
        List<SysResourceExt> list = sysResourceMapper.select4RoleSid(roleSid);
        return list2Tree(list);
    }

    @Override
    public Set<String> query4ShiroAuthorizationInfo(List<SysRoleExt> roleExtList, SysUserExt userExt) throws Exception {
        Set<String> permissions = new HashSet<>();
        List<String> roleList = new ArrayList<>();
        if (!HlpUtils.isEmptyList(roleExtList)) {
            roleList = ListUtil.map(roleExtList, SysRoleExt::getSid);
        }
        if (!HlpUtils.isEmpty(userExt.getLogin_id()) && SysConstants.LOGIN_ID4ADMIN.equals(userExt.getLogin_id())) {
            roleList = new ArrayList<>();
        }
        List<SysResourceExt> list = sysResourceMapper.select4Shiro(roleList);
        if (!HlpUtils.isEmptyList(list)) {
            for (SysResourceExt resourceExt : list) {
                permissions.add(resourceExt.getPermission());
            }
        }
        return permissions;
    }

    @Override
    public void updateSysResouceParentId(String sid, String newParentId, SysUser opUser) throws Exception {
        SysResourceExt t = sysResourceMapper.selectBySid(sid);
        if (t == null) {
            throw new HlpException("资源不存在:" + sid);
        }
        if (HlpUtils.isEmpty(newParentId, Constants.ROOT_PARENT_SID) &&
                !SysResourceExt.RES_TYPE_APPLICATION.equals(t.getRes_type()) ||
                !HlpUtils.isEmpty(newParentId, Constants.ROOT_PARENT_SID) &&
                        SysResourceExt.RES_TYPE_APPLICATION.equals(t.getRes_type())) {
            throw new HlpException("应用必须设置为第一级");
        }
        String oldParentId = t.getParent_sid();
        updateChildCount(oldParentId);
        t.setParent_sid(newParentId);
        setFullCode(t);
        sysResourceMapper.updateParent(t);
        updateChildCount(newParentId);
        if (t.getChild_count() != null && t.getChild_count() > 0) {
            List<SysResource> subList = new ArrayList<>();
            expandResource2List(t, subList);
            if (menuInSubList(t, newParentId)) {
                throw new HlpException("存在循环设置，设置失败");
            }
        }
        updateChildFullCode(sid);
        updateRoleResourceCheckType(newParentId);
    }

    @Override
    public List<SysResourceExt> findSysResource4User(String userSid) throws Exception {
        return sysResourceMapper.select4UserId(userSid);
    }

    @Override
    public List<SysResourceExt> queryValidApps(SysUser opUser) throws Exception {
        return sysResourceMapper.selectValidApplications(opUser.getSid());
    }


    /**
     * 设置下级资源数量
     *
     * @param sid
     * @throws HlpException
     */
    private void updateChildCount(String sid) throws HlpException {
        if (!HlpUtils.isEmpty(sid) && !Constants.ROOT_PARENT_SID.equals(sid)) {
            SysResource parent = sysResourceMapper.selectBySid(sid);
            if (parent != null) {
                int count = sysResourceMapper.countByParentSid(parent.getSid());
                parent.setChild_count(count);
                sysResourceMapper.updateByPrimaryKeySelective(parent);
            } else {
                throw new HlpException("资源sid不存在：" + sid);
            }
        }
    }

    /**
     * 根据上级sid更新fullCode（递归方法）
     *
     * @param newParentId
     */
    private void updateChildFullCode(String newParentId) {
        SysResource parent = HlpUtils.isEmpty(newParentId) ? null : sysResourceMapper.selectBySid(newParentId);
        List<SysResourceExt> children = sysResourceMapper.selectByParentSid(newParentId);
        if (children != null && children.size() > 0) {
            for (SysResource t : children) {
                t.setFull_code(parent == null ? t.getCode() + "." : parent.getFull_code() + t.getCode() + ".");
                sysResourceMapper.updateByPrimaryKeySelective(t);
                if (t.getChild_count() != null && t.getChild_count() > 0) {
                    updateChildFullCode(t.getSid());
                }
            }
        }
    }

    /**
     * 展开到一个列表中
     *
     * @param t
     */
    private void expandResource(SysResourceExt t) {
        if (t.getChild_count() != null && t.getChild_count() > 0) {
            List<SysResourceExt> items = sysResourceMapper.selectByParentSid(t.getSid());
            t.setChildrens(items);
            for (SysResourceExt c : items) {
                expandResource(c);
            }
        }
    }

    private void expandResource(SysResourceExt t, List<String> validList) {
        if (t.getChild_count() != null && t.getChild_count() > 0) {
            List<SysResourceExt> items = sysResourceMapper.selectByParentSid(t.getSid());
            List<SysResourceExt> validItems = new ArrayList<>();
            for (SysResourceExt resource : items) {
                if (validList.contains(resource.getSid())) {
                    validItems.add(resource);
                }
            }
            t.setChildrens(validItems);
            for (SysResourceExt c : validItems) {
                expandResource(c, validList);
            }
        }
    }

    /**
     * 展开到一个列表中。递归调用。
     *
     * @param m
     */
    private void expandResource2List(SysResource m, List<SysResource> subList) {
        if (m.getChild_count() != null && m.getChild_count() > 0) {
            List<SysResourceExt> children = sysResourceMapper.selectByParentSid(m.getSid());
            if (children != null && children.size() > 0) {
                subList.addAll(children);
                for (SysResource c : children) {
                    expandResource2List(c, subList);
                }
            }
        }
    }

    /**
     * 判断目标菜单ID是否存在源菜单的子菜单中
     *
     * @param source
     * @param targetMenuId
     * @return
     */
    private boolean menuInSubList(SysResourceExt source, String targetMenuId) {
        boolean ret = false;
        if (source.getChildrens() != null && source.getChildrens().size() > 0) {
            for (SysResource t : source.getChildrens()) {
                if (t.getSid().equals(targetMenuId)) {
                    ret = true;
                    break;
                }
            }
        }
        return ret;
    }
}
